
/**
  ******************************************************************************
  * Copyright 2021 The Microbee Authors. All Rights Reserved.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  * 
  * http://www.apache.org/licenses/LICENSE-2.0
  * 
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  * 
  * @file       soft_sigreader_int.c
  * @author     baiyang
  * @date       2022-6-22
  ******************************************************************************
  */

/*----------------------------------include-----------------------------------*/
#include "soft_sigreader_int.h"

#include <board.h>

#if defined(HAL_USE_EICU)
/*-----------------------------------macro------------------------------------*/
#ifndef RCININT_EICU_CHANNEL
#define RCININT_EICU_CHANNEL TIM_CHANNEL_1
#endif
/*----------------------------------typedef-----------------------------------*/
typedef struct soft_sigreader_int* soft_sigreader_int_t;

#pragma pack(1)
typedef struct {
    uint16_t w0;
    uint16_t w1;
} ssi_pulse_t;
#pragma pack()

struct soft_sigreader_int {
    struct rt_ringbuffer sigbuf;
    uint8_t sigbuf_pool[SOFTSIG_MAX_SIGNAL_TRANSITIONS * sizeof(ssi_pulse_t)];

    uint32_t channel;
    uint32_t aux_channel;

    uint16_t last_value;
};

/*---------------------------------prototype----------------------------------*/
static uint64_t get_timer_clock(TIM_HandleTypeDef *htim);
static uint32_t get_pair_channel(uint32_t channel);
static inline uint32_t get_channel_num(uint32_t channel);
static void MX_TIM12_Init(void);
/*----------------------------------variable----------------------------------*/
static struct soft_sigreader_int ssi;
static TIM_HandleTypeDef htim12;
/*-------------------------------------os-------------------------------------*/

/*----------------------------------function----------------------------------*/
void ssi_init()
{
    ssi.channel = RCININT_EICU_CHANNEL;
    ssi.aux_channel = get_pair_channel(ssi.channel);

    ssi.last_value = 0;
    rt_ringbuffer_init(&ssi.sigbuf, ssi.sigbuf_pool, sizeof(ssi.sigbuf_pool));

    MX_TIM12_Init();
    HAL_TIM_Base_Start_IT(&htim12);

    HAL_TIM_IC_Start_IT(&htim12,ssi.channel);
    HAL_TIM_IC_Start_IT(&htim12,ssi.aux_channel);
}

bool ssi_read(uint32_t *widths0, uint32_t *widths1)
{
    /* disable interrupt */
    rt_base_t level = rt_hw_interrupt_disable();

    if (rt_ringbuffer_data_len(&ssi.sigbuf) >= 2 * sizeof(ssi_pulse_t)) {
        ssi_pulse_t pulse;
        if (rt_ringbuffer_get(&ssi.sigbuf, (uint8_t *)&pulse, sizeof(ssi_pulse_t))) {
            *widths0 = (uint32_t)((uint16_t)(pulse.w0 - ssi.last_value));
            *widths1 = (uint32_t)((uint16_t)(pulse.w1 - pulse.w0));
            ssi.last_value = pulse.w1;

            /* enable interrupt */
            rt_hw_interrupt_enable(level);
            return true;
        }
    }

    /* enable interrupt */
    rt_hw_interrupt_enable(level);

    return false;
}

/**
  * @brief This function handles TIM8 break interrupt and TIM12 global interrupt.
  */
void TIM8_BRK_TIM12_IRQHandler(void)
{
    /* USER CODE BEGIN TIM8_BRK_TIM12_IRQn 0 */
     /* enter interrupt */
    rt_interrupt_enter();
    /* USER CODE END TIM8_BRK_TIM12_IRQn 0 */

    HAL_TIM_IRQHandler(&htim12);

    /* USER CODE BEGIN TIM8_BRK_TIM12_IRQn 1 */
    /* leave interrupt */
    rt_interrupt_leave();
    /* USER CODE END TIM8_BRK_TIM12_IRQn 1 */
}

//TIM1-12 CH3
//TIM1-34 CH4
//TIM3-12 CH1
//TIM3-34 CH2
//TIM15-12 CH5
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
    if(TIM12 == htim->Instance && htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2){
        ssi_pulse_t pulse;

        pulse.w0 = HAL_TIM_ReadCapturedValue(htim,ssi.channel);
        pulse.w1 = HAL_TIM_ReadCapturedValue(htim,ssi.aux_channel);

        rt_ringbuffer_put(&ssi.sigbuf, (uint8_t *)&pulse, sizeof(ssi_pulse_t));

        //check for missed interrupt
        uint32_t mask = (TIM_SR_CC1OF << get_channel_num(ssi.channel)) | (TIM_SR_CC1OF << get_channel_num(ssi.aux_channel));
        if ((htim->Instance->SR & mask) != 0) {
            //we have missed some pulses
            //try to reset RCProtocol parser by returning invalid value (i.e. 0 width pulse)
            pulse.w0 = 0;
            pulse.w1 = 0;
            rt_ringbuffer_put(&ssi.sigbuf, (uint8_t *)&pulse, sizeof(ssi_pulse_t));
            //reset overcapture mask
            htim->Instance->SR &= ~mask;
        }
    }
}

/* APBx timer clocks frequency doubler state related to APB1CLKDivider value */
static void pclkx_doubler_get(uint32_t *pclk1_doubler, uint32_t *pclk2_doubler)
{
    uint32_t flatency = 0;
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

    RT_ASSERT(pclk1_doubler != RT_NULL);
    RT_ASSERT(pclk1_doubler != RT_NULL);

    HAL_RCC_GetClockConfig(&RCC_ClkInitStruct, &flatency);

    *pclk1_doubler = 1;
    *pclk2_doubler = 1;

#if defined(SOC_SERIES_STM32MP1)
    if (RCC_ClkInitStruct.APB1_Div != RCC_APB1_DIV1)
    {
        *pclk1_doubler = 2;
    }
    if (RCC_ClkInitStruct.APB2_Div != RCC_APB2_DIV1)
    {
       *pclk2_doubler = 2;
    }
#else
    if (RCC_ClkInitStruct.APB1CLKDivider != RCC_HCLK_DIV1)
    {
         *pclk1_doubler = 2;
    }
#if !defined(SOC_SERIES_STM32F0) && !defined(SOC_SERIES_STM32G0)
    if (RCC_ClkInitStruct.APB2CLKDivider != RCC_HCLK_DIV1)
    {
         *pclk2_doubler = 2;
    }
#endif
#endif
}

static uint64_t get_timer_clock(TIM_HandleTypeDef *htim)
{
    uint64_t tim_clock = 0;
    uint32_t pclk1_doubler, pclk2_doubler;

    pclkx_doubler_get(&pclk1_doubler, &pclk2_doubler);

#if defined(SOC_SERIES_STM32F2) || defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7)
    if (htim->Instance == TIM9 || htim->Instance == TIM10 || htim->Instance == TIM11 || htim->Instance == TIM1 || htim->Instance == TIM8)
#elif defined(SOC_SERIES_STM32L4)
    if (htim->Instance == TIM15 || htim->Instance == TIM16 || htim->Instance == TIM17)
#elif defined(SOC_SERIES_STM32MP1)
    if (htim->Instance == TIM4)
#elif defined(SOC_SERIES_STM32F1) || defined(SOC_SERIES_STM32F0) || defined(SOC_SERIES_STM32G0)
    if (0)
#endif
    {
#if !defined(SOC_SERIES_STM32F0) && !defined(SOC_SERIES_STM32G0)
        tim_clock = HAL_RCC_GetPCLK2Freq() * pclk2_doubler;
#endif
    }
    else
    {
#if defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32F0) || defined(SOC_SERIES_STM32G0)
        tim_clock = HAL_RCC_GetPCLK1Freq();
#else
        tim_clock = HAL_RCC_GetPCLK1Freq() * pclk1_doubler;
#endif
    }

    return tim_clock;
}

static uint32_t get_pair_channel(uint32_t channel)
{
    switch (channel) {
        case TIM_CHANNEL_1:
            return TIM_CHANNEL_2;
        case TIM_CHANNEL_2:
            return TIM_CHANNEL_1;
        case TIM_CHANNEL_3:
            return TIM_CHANNEL_4;
        case TIM_CHANNEL_4:
            return TIM_CHANNEL_3;
    }
    return TIM_CHANNEL_4;
}

static inline uint32_t get_channel_num(uint32_t channel)
{
    switch (channel) {
        case TIM_CHANNEL_1:
            return 0;
        case TIM_CHANNEL_2:
            return 1;
        case TIM_CHANNEL_3:
            return 2;
        case TIM_CHANNEL_4:
            return 3;
    }
    return 3;
}

/**
  * @brief TIM12 Initialization Function
  * @param None
  * @retval None
  */
static void MX_TIM12_Init(void)
{

    /* USER CODE BEGIN TIM12_Init 0 */

    /* USER CODE END TIM12_Init 0 */

    TIM_ClockConfigTypeDef sClockSourceConfig = {0};
    TIM_IC_InitTypeDef sConfigIC = {0};

    /* USER CODE BEGIN TIM12_Init 1 */

    /* USER CODE END TIM12_Init 1 */
    htim12.Instance = TIM12;
    htim12.Init.Prescaler = (uint32_t)(get_timer_clock(&htim12) / INPUT_CAPTURE_FREQUENCY - 1);
    htim12.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim12.Init.Period = INPUT_CAPTURE_MAX_PERIOD;
    htim12.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    htim12.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
    if (HAL_TIM_Base_Init(&htim12) != HAL_OK)
    {
        Error_Handler();
    }
    sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
    if (HAL_TIM_ConfigClockSource(&htim12, &sClockSourceConfig) != HAL_OK)
    {
        Error_Handler();
    }
    if (HAL_TIM_IC_Init(&htim12) != HAL_OK)
    {
        Error_Handler();
    }

#if defined(HAL_RCIN_IS_INVERTED)
    sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
#else
    sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_FALLING;
#endif
    sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
    sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
    sConfigIC.ICFilter = 2;
    if (HAL_TIM_IC_ConfigChannel(&htim12, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
    {
        Error_Handler();
    }
#if defined(HAL_RCIN_IS_INVERTED)
    sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_FALLING;
#else
    sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
#endif
    sConfigIC.ICSelection = TIM_ICSELECTION_INDIRECTTI;
    sConfigIC.ICFilter = 0;
    if (HAL_TIM_IC_ConfigChannel(&htim12, &sConfigIC, TIM_CHANNEL_2) != HAL_OK)
    {
        Error_Handler();
    }
    /* USER CODE BEGIN TIM12_Init 2 */

    /* USER CODE END TIM12_Init 2 */

}
/*------------------------------------test------------------------------------*/
#endif

